package lispinterpreter;

public class ElementaryFunctions {

    public static LispList evaluate(LispList list) throws ParametersException {
        LispList result;
        LispAtom atom = (LispAtom) list.getFirstElement();
        String operator = atom.satom;
    
        if (atom.getType() == LispAtom.ATOM_TYPE_STRING) {
            if (operator.equals("+"))
        	result = sum(list);
            else if (operator.equals("-"))
        	result = sub(list);
            else if (operator.equals("/"))
        	result = div(list);
            else if (operator == "*")
        	result = mult(list);
            else if (operator == "<")
        	result = lessThan(list);
            else if (operator == ">")
        	result = greaterThan(list);
            else if (operator == "=")
        	result = equals(list);
            else if (operator == "eq")
        	result = equivalent(list);
            else
        	throw new ParametersException("Operator not valid. " + operator);
    
        } else
            throw new ParametersException("Parameter missing.");
    
        return result;
    }

    private static LispList sum(LispList list) throws ParametersException {
        if (!list.isParameter(LispAtom.ATOM_TYPE_FLOAT)) {
            throw new ParametersException(
        	    "Wrong parameter handed, required float.");
        }
        int len = list.size();
        LispAtom atom;
        float result = 0;
        for (int i = 1; i < len; i++) {
            atom = (LispAtom) list.getElement(i);
            result = result + atom.fatom;
        }
        atom = new LispAtom(result);
        return atom;
    }

    private static LispList sub(LispList list) throws ParametersException {
        if (!list.isParameter(LispAtom.ATOM_TYPE_FLOAT)) {
            throw new ParametersException(
        	    "Wrong parameter handed, required float.");
        }
        int len = list.size();
        LispAtom atom;
        float result = 0;
        for (int i = 1; i < len; i++) {
            atom = (LispAtom) list.getElement(i);
            if (i == 1)
        	result = atom.fatom;
            else
        	result = result - atom.fatom;
        }
        atom = new LispAtom(result);
        return atom;
    }

    // Multiplicación
    private static LispList mult(LispList list) throws ParametersException {
        if (!list.isParameter(LispAtom.ATOM_TYPE_FLOAT)) {
            throw new ParametersException(
        	    "Wrong parameter handed, required float.");
        }
        int len = list.size();
        LispAtom atom;
        float result = 1;
        for (int i = 1; i < len; i++) {
            atom = (LispAtom) list.getElement(i);
            result = result * atom.fatom;
        }
        atom = new LispAtom(result);
        return atom;
    }

    private static LispList div(LispList list) throws ParametersException {
	if (!list.isParameter(0)) {
	    throw new ParametersException(
		    "Wrong parameter handed, required float.");
	}
	int len = list.size();
	LispAtom atom;
	float result = 1;
	for (int i = 1; i < len; i++) {
	    atom = (LispAtom) list.getElement(i);
	    if (i == 1)
		result = atom.fatom;
	    else
		result = result / atom.fatom;
	}
	atom = new LispAtom(result);
	return atom;
    }

    private static LispList equals(LispList list) throws ParametersException {
	if (!list.isParameter(0)) {
	    throw new ParametersException(
		    "Wrong parameter handed, required float.");
	}
	int len = list.size();
	LispAtom atom1;
	LispAtom atom2;
	boolean result = true;
	for (int i = 1; i < len - 1; i++) {
	    atom1 = (LispAtom) list.getElement(i);
	    atom2 = (LispAtom) list.getElement(i + 1);
	    result = result && (atom1.fatom == atom2.fatom);
	}
	atom1 = new LispAtom(result);
	return atom1;
    }

    private static LispList equivalent(LispList list)
	    throws ParametersException {
	int len = list.size();
	boolean result = true;
	LispAtom atom = (LispAtom) list.getElement(1);
	int type = atom.getType();

	LispAtom atom1;
	LispAtom atom2;

	for (int i = 1; i < len - 1; i++) {
	    atom1 = (LispAtom) list.getElement(i);
	    atom2 = (LispAtom) list.getElement(i + 1);

	    switch (type) {
	    // float
	    case LispAtom.ATOM_TYPE_FLOAT:
		if (!list.isParameter(LispAtom.ATOM_TYPE_FLOAT))
		    result = false;
		result = result && (atom1.fatom == atom2.fatom);
		break;
	    // boolean
	    case LispAtom.ATOM_TYPE_BOOLEAN:
		if (!list.isParameter(LispAtom.ATOM_TYPE_BOOLEAN))
		    result = false;
		result = result && (atom1.batom == atom2.batom);
		break;
	    // string
	    case LispAtom.ATOM_TYPE_STRING:
		if (!list.isParameter(LispAtom.ATOM_TYPE_STRING))
		    result = false;
		result = result && (atom1.satom.equals(atom2.satom));
		break;
	    }
	}
	atom1 = new LispAtom(result);
	return atom1;
    }

    private static LispList lessThan(LispList list) throws ParametersException {
	if (!list.isParameter(0)) {
	    throw new ParametersException(
		    "Wrong parameter handed, required float.");
	}
	int len = list.size();
	LispAtom atom1;
	LispAtom atom2;
	boolean result = true;
	for (int i = 1; i < len - 1; i++) {
	    atom1 = (LispAtom) list.getElement(i);
	    atom2 = (LispAtom) list.getElement(i + 1);
	    result = result && (atom1.fatom < atom2.fatom);
	}
	atom1 = new LispAtom(result);
	return atom1;
    }

    private static LispList greaterThan(LispList list)
            throws ParametersException {
        if (!list.isParameter(0)) {
            throw new ParametersException(
        	    "Wrong parameter handed, required float.");
        }
        int len = list.size();
        LispAtom atom1;
        LispAtom atom2;
        boolean result = true;
        for (int i = 1; i < len - 1; i++) {
            atom1 = (LispAtom) list.getElement(i);
            atom2 = (LispAtom) list.getElement(i + 1);
            result = result && (atom1.fatom > atom2.fatom);
        }
        atom1 = new LispAtom(result);
        return atom1;
    }
}
